﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Caca.MineSweeper
{
    public class Game
    {
        public int TotalMines { get; protected set; }
        public int RemainMines
        {
            get { return TotalMines - CoverPlots.Count(p => p is MarkedPlot); }
        }
        public int FieldWidth { get; private set; }
        public int FieldHeight { get; private set; }
        public GameStatus Status { get; private set; }

        protected List<Plot> CoverPlots { get; set; }
        protected MineField Field = null;

        public Game(MineField field)
        {
            if (field == null)
                throw new ArgumentNullException("field");

            this.Field = field;
            this.TotalMines = this.Field.Plots.Count(p => p is MinePlot);
            this.FieldHeight = this.Field.Height;
            this.FieldWidth = this.Field.Width;
            this.CoverPlots = new List<Plot>();
            for (int i = 0; i < this.Field.Plots.Count; i++)
                this.CoverPlots.Add(new CoveredPlot());
        }

        public Plot GetPlot(int x, int y)
        {
            ValidPosition(x, y);

            var c = GetCoverPlot(x, y);
            var result = c == null ? GetFieldPlot(x, y) : c;
            return result;
        }

        /// <summary>
        /// 打开指定格子
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public Plot UnCover(int x, int y)
        {
            ValidPosition(x, y);

            var index = PositionToIndex(x, y);

            Plot result = null;

            if (this.CoverPlots[index] is MarkedPlot)
            {
                result = this.CoverPlots[index];
            }
            else
            {
                this.CoverPlots[index] = null;
                result = GetFieldPlot(x, y);
            }

            if (result is MinePlot)
            {
                Status = GameStatus.Lose;
            }
            else if (result is NonMinePlot && TotalMines == CoverPlots.Count(p => p != null))
            {
                Status = GameStatus.Win;
            }

            return result;
        }

        #region private

        private Plot GetFieldPlot(int x, int y)
        {
            return this.Field.Plots[PositionToIndex(x, y)];
        }

        private Plot GetCoverPlot(int x, int y)
        {
            return this.CoverPlots[PositionToIndex(x, y)];
        }

        private int PositionToIndex(int x, int y)
        {
            return x + y * FieldWidth;
        }

        private void ValidPosition(int x, int y)
        {
            if (x >= FieldWidth || y >= FieldHeight || x < 0 || y < 0)
                throw new ArgumentOutOfRangeException();
        }

        #endregion

        public Plot Mark(int x, int y)
        {
            ValidPosition(x, y);

            var result = GetPlot(x, y);

            if (result is CoveredPlot)
            {
                result = new MarkedPlot();
                CoverPlots[PositionToIndex(x, y)] = result;
            }

            return result;
        }

        public Plot UnMark(int x, int y)
        {
            ValidPosition(x, y);

            var index = PositionToIndex(x, y);

            if (CoverPlots[index] is MarkedPlot)
            {
                CoverPlots[index] = new CoveredPlot();
            }

            return GetPlot(x, y);
        }
    }
}
